#-----------------------------------------------------------------
# pycparser: __init__.py
#
# This package file exports some convenience functions for 
# interacting with pycparser
#
# Copyright (C) 2008-2011, Eli Bendersky
# License: BSD
#-----------------------------------------------------------------

__all__ = ['c_lexer', 'c_parser', 'c_ast']
__version__ = '2.04'

from subprocess import Popen, PIPE

from .c_parser import CParser
import shutil
import os
import subprocess



invalid_tokens = [ '__extension__', '__off64_t', '__quad_t', '__off_t', '_G_int16_t', '_G_int32_t', '_G_uint16_t', '_G_uint32_t', '__builtin_va_list', '__ssize_t', '__const', \
		'__gnuc_va_list', '_G_fpos_t', '__WAIT_STATUS', '__attribute__', '__u_quad_t', '__fsid_t', '__loff_t', '__ino_t', '__dev_t', \
		'__gid_t', '__mode_t mode_t', '__nlink_t', '__uid_t', '__pid_t', '__id_t', '__daddr_t', '__caddr_t','__key_t','__clock_t','__time_t','__clockid_t','__timer_t', \
		'__suseconds_t', '__blksize_t', '__blkcnt_t', '__fsblkcnt_t', '__fsfilcnt_t' \
		]


def extract_includes(filename):
	includes=[]
	in_file=open(filename,'r')
	for line in in_file:
		if(line.find('#') != -1):
			index=line.find('#')
			while(True):
				if(line[0] == '#'):
					line=line[1:]
					break
				if(not line[0].isspace()):
					print('expected space, but found '+line[0]+' at start of line '+line)
					quit()
				line=line[1:]
			while(line[0].isspace()):
				line=line[1:]
			if(line[0:7] == 'include'):
				line=line[7:]
				while(line[0].isspace()):
					line=line[1:]
				assert(line[0] == '\"' or line[0] == '<')
				type_of_include=line[0]
				line = line[1:]
				if(type_of_include == '\"'):
					pos = line.find('\"')
				else:
					pos = line.find('>')
				line = line[:pos]
				if(line[0] != '/'):
					line = './'+line
				if(not line in includes):
					includes.append(line)
				
	in_file.close()
	return includes

def get_absolute_filename(f, include_paths):
	current_dir= os.path.abspath('.')
	if(f[0] == '/'):
		return f
	elif(os.path.exists(current_dir+'/'+f)):
		return current_dir+'/'+f
	else:
		for path in include_paths:
			tmp_path = path
			if(tmp_path[len(tmp_path)-1] != '/'):
				tmp_path = tmp_path + '/'
			if(os.path.exists(tmp_path+f)):
				return tmp_path+f
	return None

def remove_comments(filename, include_paths):
	# remccoms3.sed gotten from http://sed.sourceforge.net/grabbag/scripts/remccoms3.sed
	# discovered through http://stackoverflow.com/questions/241327/python-snippet-to-remove-c-and-c-comments
	assert(filename[len(filename)-2:] == '.c' or filename[len(filename)-2:] == '.h')
	new_file_name = filename[0:len(filename)-2].replace('/','___')
	if(new_file_name[0] == '.'):
		new_file_name = new_file_name[1:]
	new_file_name = new_file_name+'###without_comments###.'+filename[len(filename)-1]
	dest = open(new_file_name,'wb')
	tmp_filename = get_absolute_filename(filename, include_paths)
	if(tmp_filename == None):
		print("Unable to find file "+filename)
		return None
		#exit()
	src = open(tmp_filename,'rb')
	process = subprocess.Popen(['./remccoms3.sed'],stdin=src,stdout=dest)
	return_code=process.wait()
	dest.close()
	src.close()
	return new_file_name


def parse_multi_files(filenames,include_paths,defines, prefix, use_cpp=False,cpp_path='cpp',cpp_args=''):

	for f in filenames:
		system_call = 'cpp '
		tmp_f = get_absolute_filename(f, include_paths)

		if(tmp_f == None):
			print('Unable to find file '+f+' in include paths')
			quit()

		system_call += '\"'+tmp_f+'\"'
		for i in include_paths:
			system_call = system_call + ' -I ' + '\"'+i+'\"'
		for d in defines:
			system_call = system_call + ' -D'+d
		system_call = system_call + ' > '+tmp_f.replace('/','___')+'.tmp'
		ret_val = os.system(system_call)
		if(ret_val != 0):
			print('Error '+str(ret_val)+' in call to cpp')
			quit()

		fp = open(tmp_f.replace('/','___')+'.tmp','r')
		extracted = []
		useful = False
		lines = fp.readlines()
		index = 0

		while(index < len(lines)):
			l = lines[index]
			if(l[0] == '#'):
				line_type = None
				tokens = l.split(' ')
				line_num = int(tokens[1])
				tmp_filename = tokens[2]
				if(tmp_filename[len(tmp_filename)-1] == '\n'):
					tmp_filename=tmp_filename[0:len(tmp_filename)-1]
				if(len(tokens) > 3):
					line_type = tokens[3]
				if(tmp_filename == '\"'+tmp_f+'\"'):
					useful = True
				else:
					useful = False
				index = index + 1
			elif(l.find('typedef') != -1):
				acc_lines = [ ]
				increment = 0

				increment = increment + lines[index].count('{')
				increment = increment - lines[index].count('}')

				acc_lines.append(lines[index])

				if(not(increment == 0 and lines[index].find(';') != -1)):
					while(index < len(lines)-1 and not (increment == 0 and lines[index].find(';') != -1)):
						index = index + 1
						
						increment = increment + lines[index].count('{')
						increment = increment - lines[index].count('}')
						acc_lines.append(lines[index])

				contains_invalid_tokens = False

				for tmp_l in acc_lines:
					for t in invalid_tokens:
						if(tmp_l.find(t) != -1):
							contains_invalid_tokens = True
							break
					if(contains_invalid_tokens == True):
						break
					
				if(not contains_invalid_tokens):
					for tmp_l in acc_lines:
						extracted.append(tmp_l)
				index = index + 1
			else:
				if(useful):
					extracted.append(l)
				index = index + 1

		fp.close()
		os.remove(tmp_f.replace('/','___')+'.tmp')

		fp = open(tmp_f.replace('/','___')+'.useful.'+f[len(f)-1],'w')
		for l in extracted:
			fp.write(l)
		fp.close()

	if(os.path.exists(prefix+'concat.c')):
		os.remove(prefix+'concat.c')
	output_file = open(prefix+'concat.c','w')
	for f in filenames:
		tmp_f = get_absolute_filename(f, include_paths)
		fp = open(tmp_f.replace('/','___')+'.useful.'+f[len(f)-1], 'r')
		data = fp.read()
		fp.close()
		os.remove(tmp_f.replace('/','___')+'.useful.'+f[len(f)-1])
		output_file.write(data)

	output_file.close()
		
	ast = parse_file(prefix+'concat.c',use_cpp,cpp_path,cpp_args)

	os.remove(prefix+'concat.c')
	os.remove(os.getcwd()+'/lextab.py')
	os.remove(os.getcwd()+'/yacctab.py')
	compiled_files=os.listdir(prefix)
	for f in compiled_files:
		if(f[len(f)-4:len(f)] == '.pyc'):
			os.remove(prefix+f)
	return ast

def parse_file( filename, use_cpp=False, 
                cpp_path='cpp', cpp_args=''):
    """ Parse a C file using pycparser.
    
        filename:
            Name of the file you want to parse.
        
        use_cpp:
            Set to True if you want to execute the C pre-processor
            on the file prior to parsing it.
        
        cpp_path:
            If use_cpp is True, this is the path to 'cpp' on your
            system. If no path is provided, it attempts to just
            execute 'cpp', so it must be in your PATH.
        
        cpp_args:
            If use_cpp is True, set this to the command line 
            arguments strings to cpp. Be careful with quotes - 
            it's best to pass a raw string (r'') here. 
            For example:
            r'-I../utils/fake_libc_include'
            If several arguments are required, pass a list of 
            strings.
        
        When successful, an AST is returned. ParseError can be 
        thrown if the file doesn't parse successfully.
        
        Errors from cpp will be printed out. 
    """
    if use_cpp:   
        path_list = [cpp_path]
        if isinstance(cpp_args, list):
            path_list += cpp_args
        elif cpp_args != '': 
            path_list += [cpp_args]
        path_list += [filename]
        
        # Note the use of universal_newlines to treat all newlines
        # as \n for Python's purpose
        #
        pipe = Popen(   path_list, 
                        stdout=PIPE, 
                        universal_newlines=True)
        text = pipe.communicate()[0]
    else:
        text = open(filename, 'rU').read()
    
    parser = CParser()
    return parser.parse(text, filename)
    

if __name__ == "__main__":
    pass
    

